package com.quectel.sdk.simulation;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Maps;
import com.quectel.base.tuple.Tuple2;
import com.quectel.base.tuple.Tuples;
import com.quectel.sdk.ex.simulation.TokenException;
import com.quectel.util.common.Response;
import com.quectel.util.ex.CheckErrorException;
import com.quectel.util.http.HttpRestUtils;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * 在这里编写说明
 *
 * @author: rananxin
 * @email: ryan.ran@quectel.com
 * @date: 2022-10-19 11:01:00
 */
public class SimulationRequester {

    private static final Logger logger = LoggerFactory.getLogger(SimulationRequester.class);


    private final static String CONTENT_TYPE = "application/json";

    /**
     * cache
     * k1 url
     * k2 key
     */
    private static Function<Tuple2<String, String>, String> TOKEN_KEY_FUNC = (t) ->
            "url:" + t.getT1() + ":" + t.getT2();

    private static final Cache<String, String> TOKEN_CACHE =
            CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build();

    private static final HttpRestUtils HTTP_REST_UTILS = HttpRestUtils.getTrustSSLInstance();

    private String url;
    private String accessKey;
    private String accessSecret;


    private SimulationRequester(String url, String accessKey, String accessSecret) {
        this.url = url;
        this.accessKey = accessKey;
        this.accessSecret = accessSecret;
    }


    public static SimulationRequester getInstance(String url, String appKey, String appSecret) {

        return new SimulationRequester(url, appKey, appSecret);
    }

    public <T> Response<T> get(String path, Map<String, Object> params, TypeReference<Response<T>> typeReference) {
        if (this.url == null) {
            throw new CheckErrorException("checked url " + this.url + " is null");
        }
        String token = getToken();
        if (token == null) {
            throw new TokenException("Obtain simulation platform token failed");
        }

        List<NameValuePair> paramList = new ArrayList<>();
        if (params != null) {
            Set<Map.Entry<String, Object>> entries = params.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                paramList.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
            }
        }

        Map<String, String> headers = new HashMap<>();
        headers.put("token", token);
        Response<T> response = HTTP_REST_UTILS.get(url + path, paramList, headers, typeReference).getRight();
        if (response.getCode() == 20001 || response.getCode() == 20002) {
            TOKEN_CACHE.asMap().remove(TOKEN_KEY_FUNC.apply(Tuples.of(url, accessKey)));
            token = getToken();
            headers.put("token", token);
            response = HTTP_REST_UTILS.get(url + path, paramList, headers, typeReference).getRight();
        }
        return response;

    }

    public <T> Response<T> post(String path, Object params, TypeReference<Response<T>> typeReference) {
        if (this.url == null) {
            throw new CheckErrorException("checked url " + this.url + " is null");
        }
        String token = getToken();
        if (token == null) {
            throw new TokenException("Obtain simulation platform token failed");
        }
        Map<String, String> headers = new HashMap<>();
        headers.put("token", token);
        Response<T> response = HTTP_REST_UTILS.post(url + path, params, headers, typeReference).getRight();
        if (response.getCode() == 20001 || response.getCode() == 20002) {
            TOKEN_CACHE.asMap().remove(TOKEN_KEY_FUNC.apply(Tuples.of(url, accessKey)));
            token = getToken();
            headers.put("token", token);
            response = HTTP_REST_UTILS.post(url + path, params, headers, typeReference).getRight();
        }
        return response;

    }

    public String getToken() {
        return TOKEN_CACHE.asMap().computeIfAbsent(
                TOKEN_KEY_FUNC.apply(Tuples.of(url, accessKey)),
                (cacheKey) -> {
                    String apiPath = "/access/auth";
                    Map<String, Object> params = Maps.newHashMap();
                    params.put("accessKey", accessKey);
                    params.put("accessSecret", accessSecret);
                    Response<String> response = HTTP_REST_UTILS.post(url + apiPath, params, null, new TypeReference<Response<String>>() {
                    }).getRight();
                    if (response.isSuccess()) {
                        return response.getData();
                    }
                    return null;
                }
        );
    }


}
